import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cs-cornerstone-tools';
import OHIF from '@ohif/core';
import DEFAULT_CONTEXT from './defaultContext';

import setCornerstoneLayout from './utils/setCornerstoneLayout.js';
import { getEnabledElement } from './state';
const scroll = cornerstoneTools.import('util/scroll');

const { studyMetadataManager } = OHIF.utils;
const { setViewportSpecificData } = OHIF.redux.actions;

const refreshCornerstoneViewports = () => {
  cornerstone.getEnabledElements().forEach(enabledElement => {
    if (enabledElement.image) {
      cornerstone.updateImage(enabledElement.element);
    }
  });
};

const getActiveEnabledElement = viewports => {
  const modeViewports = viewports[viewports.viewMode];
  const viewportIndex = modeViewports.activeViewportIndex;
  const imageViewportIndex = modeViewports.layout.viewports[viewportIndex].activeViewportIndex || 0;
  return getEnabledElement(viewportIndex, imageViewportIndex);
};

const commandsModule = ({ servicesManager }) => {
  const actions = {
    rotateViewport: ({ viewports, rotation }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);
        viewport.rotation += rotation;
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    flipViewportHorizontal: ({ viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);
        viewport.hflip = !viewport.hflip;
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    flipViewportVertical: ({ viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);
        viewport.vflip = !viewport.vflip;
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    scaleViewport: ({ direction, viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);
      const step = direction * 0.15;

      if (enabledElement) {
        if (step) {
          let viewport = cornerstone.getViewport(enabledElement);
          viewport.scale += step;
          cornerstone.setViewport(enabledElement, viewport);
        } else {
          cornerstone.fitToWindow(enabledElement);
        }
      }
    },
    resetViewport: ({ viewports }) => {
      cornerstoneTools.setToolActive('Select', { mouseButtonMask: 1 });
      const enabledElement = getActiveEnabledElement(viewports);
      if (enabledElement) {
        cornerstone.reset(enabledElement);
      }
    },
    resetWwwc: ({ viewports }) => {
      const element = getActiveEnabledElement(viewports);

      if (element) {
        const enabledElement = cornerstone.getEnabledElement(element);
        let viewport = cornerstone.getViewport(element);
        if (enabledElement && enabledElement.image) {
          const image = enabledElement.image;
          viewport.voi = {
            windowWidth: parseFloat(image.windowWidth),
            windowCenter: parseFloat(image.windowCenter),
          };
          cornerstone.setViewport(element, viewport);
        }
      }
    },
    resetRotate: ({ viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);
        viewport.hflip = false;
        viewport.vflip = false;
        viewport.rotation = 0;
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    invertViewport: ({ viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);
        viewport.invert = !viewport.invert;
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    downloadDICOM: ({ viewports }) => {
      const element = getActiveEnabledElement(viewports);

      const enabledElement = cornerstone.getEnabledElement(element);
      if (!enabledElement || !enabledElement.image) {
        return;
      }

      const { imageId } = enabledElement.image;
      const url = imageId.slice(imageId.indexOf(':') + 1);
      const a = document.createElement('a');
      a.href = url + '&anonymize=yes';
      a.download = `${url}.dcm`;
      a.click();
      a.remove();
    },
    downloadJPEG: ({ viewports }) => {
      const element = getActiveEnabledElement(viewports);

      const enabledElement = cornerstone.getEnabledElement(element);
      if (!enabledElement || !enabledElement.image) {
        return;
      }

      const { imageId } = enabledElement.image;
      const url = imageId.slice(imageId.indexOf(':') + 1);

      const search = url.slice(url.indexOf('?') + 1);
      const params = search.split('&');
      const map = new Map();
      params.forEach(p => {
        if (p.indexOf('=') < 0) return;
        const pair = p.split('=');
        map.set(pair[0], pair[1]);
      });
      map.delete('transferSyntax');
      map.delete('contentType');

      let jpegUrl = url.slice(0, url.indexOf('?') + 1);
      map.forEach((value, key) => {
        jpegUrl += `${key}=${value}&`;
      });
      jpegUrl = jpegUrl.slice(0, jpegUrl.length - 1);

      const a = document.createElement('a');
      a.href = jpegUrl;
      a.download = `${jpegUrl}.jpg`;
      a.click();
      a.remove();
    },
    setToolActive: ({ toolName }) => {
      if (!toolName) {
        console.warn('No toolname provided to setToolActive command');
      }
      cornerstoneTools.setToolActive(toolName, { mouseButtonMask: 1 });
    },
    clearAnnotations: ({ viewports }) => {
      const element = getActiveEnabledElement(viewports);
      if (!element) {
        return;
      }

      const enabledElement = cornerstone.getEnabledElement(element);
      if (!enabledElement || !enabledElement.image) {
        return;
      }

      const {
        toolState,
      } = cornerstoneTools.globalImageIdSpecificToolStateManager;
      if (
        !toolState ||
        toolState.hasOwnProperty(enabledElement.image.imageId) === false
      ) {
        return;
      }

      const imageIdToolState = toolState[enabledElement.image.imageId];

      Object.keys(imageIdToolState).forEach(toolType => {
        cornerstoneTools.clearToolState(element, toolType);
        cornerstone.updateImage(element);
      });
    },
    scrollToImage: ({ viewports, offset, loop }) => {
      const enabledElement = getActiveEnabledElement(viewports);
      scroll(enabledElement, offset, loop);
    },
    cinePlay: ({ viewports, isPlaying, fps }) => {
      const { viewportSpecificData, activeViewportIndex } = viewports[viewports.viewMode];
      const { cine } = viewportSpecificData[activeViewportIndex] || {};
      const playStatus = isPlaying === undefined ? ((cine && cine.isPlaying) ? false : true) : isPlaying;
      const newCineData = { cine: { isPlaying: playStatus, fps: fps || (cine && cine.fps) || 24 } };
      window.store.dispatch(
        setViewportSpecificData(activeViewportIndex, newCineData)
      );
    },
    setCineFPS: ({ viewports, fps }) => {
      const { viewportSpecificData, activeViewportIndex } = viewports[viewports.viewMode];
      const { cine } = viewportSpecificData[activeViewportIndex] || {};
      const cineData = cine || {
        isPlaying: false,
        fps: 24,
      };
      const newCineData = { cine: { ...cineData, fps } };
      window.store.dispatch(
        setViewportSpecificData(activeViewportIndex, newCineData)
      );
    },
    getActiveViewportEnabledElement: ({ viewports }) => {
      const enabledElement = getActiveEnabledElement(viewports);
      return enabledElement;
    },
    updateTableWithNewMeasurementData({
      toolType,
      measurementNumber,
      location,
      description,
    }) {
      // Update all measurements by measurement number
      const measurementApi = OHIF.measurements.MeasurementApi.Instance;
      const measurements = measurementApi.tools[toolType].filter(
        m => m.measurementNumber === measurementNumber
      );

      measurements.forEach(measurement => {
        measurement.location = location;
        measurement.description = description;

        measurementApi.updateMeasurement(measurement.toolType, measurement);
      });

      measurementApi.syncMeasurementsAndToolData();

      refreshCornerstoneViewports();
    },
    getNearbyToolData({ element, canvasCoordinates, availableToolTypes }) {
      const nearbyTool = {};
      let pointNearTool = false;

      availableToolTypes.forEach(toolType => {
        const elementToolData = cornerstoneTools.getToolState(
          element,
          toolType
        );

        if (!elementToolData) {
          return;
        }

        elementToolData.data.forEach((toolData, index) => {
          let elementToolInstance = cornerstoneTools.getToolForElement(
            element,
            toolType
          );

          if (!elementToolInstance) {
            elementToolInstance = cornerstoneTools.getToolForElement(
              element,
              `${toolType}Tool`
            );
          }

          if (!elementToolInstance) {
            console.warn('Tool not found.');
            return undefined;
          }

          if (
            elementToolInstance.pointNearTool(
              element,
              toolData,
              canvasCoordinates
            )
          ) {
            pointNearTool = true;
            nearbyTool.tool = toolData;
            nearbyTool.index = index;
            nearbyTool.toolType = toolType;
          }
        });

        if (pointNearTool) {
          return false;
        }
      });

      return pointNearTool ? nearbyTool : undefined;
    },
    removeToolState: ({ element, toolType, tool }) => {
      cornerstoneTools.removeToolState(element, toolType, tool);
      cornerstone.updateImage(element);
    },
    setCornerstoneLayout: () => {
      setCornerstoneLayout();
    },
    setWindowLevel: ({ viewports, window, level }) => {
      const enabledElement = getActiveEnabledElement(viewports);

      if (enabledElement) {
        let viewport = cornerstone.getViewport(enabledElement);

        viewport.voi = {
          windowWidth: Number(window),
          windowCenter: Number(level),
        };
        cornerstone.setViewport(enabledElement, viewport);
      }
    },
    jumpToImage: ({
      StudyInstanceUID,
      SOPInstanceUID,
      frameIndex,
      activeViewportIndex,
    }) => {
      const study = studyMetadataManager.get(StudyInstanceUID);

      const displaySet = study.findDisplaySet(ds => {
        return (
          ds.images &&
          ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
        );
      });

      displaySet.SOPInstanceUID = SOPInstanceUID;
      displaySet.frameIndex = frameIndex;

      window.store.dispatch(
        setViewportSpecificData(activeViewportIndex, displaySet)
      );

      refreshCornerstoneViewports();
    },
  };

  const definitions = {
    jumpToImage: {
      commandFn: actions.jumpToImage,
      storeContexts: [],
      options: {},
    },
    getNearbyToolData: {
      commandFn: actions.getNearbyToolData,
      storeContexts: [],
      options: {},
    },
    removeToolState: {
      commandFn: actions.removeToolState,
      storeContexts: [],
      options: {},
    },
    updateTableWithNewMeasurementData: {
      commandFn: actions.updateTableWithNewMeasurementData,
      storeContexts: [],
      options: {},
    },
    getActiveViewportEnabledElement: {
      commandFn: actions.getActiveViewportEnabledElement,
      storeContexts: ['viewports'],
      options: {},
    },
    rotateViewportCW: {
      commandFn: actions.rotateViewport,
      storeContexts: ['viewports'],
      options: { rotation: 90 },
    },
    rotateViewportCCW: {
      commandFn: actions.rotateViewport,
      storeContexts: ['viewports'],
      options: { rotation: -90 },
    },
    invertViewport: {
      commandFn: actions.invertViewport,
      storeContexts: ['viewports'],
      options: {},
    },
    resetWwwc: {
      commandFn: actions.resetWwwc,
      storeContexts: ['viewports'],
      options: {},
    },
    resetRotate: {
      commandFn: actions.resetRotate,
      storeContexts: ['viewports'],
      options: {},
    },
    flipViewportVertical: {
      commandFn: actions.flipViewportVertical,
      storeContexts: ['viewports'],
      options: {},
    },
    flipViewportHorizontal: {
      commandFn: actions.flipViewportHorizontal,
      storeContexts: ['viewports'],
      options: {},
    },
    scaleUpViewport: {
      commandFn: actions.scaleViewport,
      storeContexts: ['viewports'],
      options: { direction: 1 },
    },
    scaleDownViewport: {
      commandFn: actions.scaleViewport,
      storeContexts: ['viewports'],
      options: { direction: -1 },
    },
    fitViewportToWindow: {
      commandFn: actions.scaleViewport,
      storeContexts: ['viewports'],
      options: { direction: 0 },
    },
    resetViewport: {
      commandFn: actions.resetViewport,
      storeContexts: ['viewports'],
      options: {},
    },
    clearAnnotations: {
      commandFn: actions.clearAnnotations,
      storeContexts: ['viewports'],
      options: {},
    },
    firstImage: {
      commandFn: actions.scrollToImage,
      storeContexts: ['viewports'],
      options: { offset: -10000, loop: false },
    },
    lastImage: {
      commandFn: actions.scrollToImage,
      storeContexts: ['viewports'],
      options: { offset: 10000, loop: false },
    },
    nextImage: {
      commandFn: actions.scrollToImage,
      storeContexts: ['viewports'],
      options: { offset: 1, loop: true },
    },
    previousImage: {
      commandFn: actions.scrollToImage,
      storeContexts: ['viewports'],
      options: { offset: -1, loop: true },
    },
    cinePlay: {
      commandFn: actions.cinePlay,
      storeContexts: ['viewports'],
      options: {}
    },
    setCineFPS: {
      commandFn: actions.setCineFPS,
      storeContexts: ['viewports'],
      options: {}
    },
    // TOOLS
    setToolActive: {
      commandFn: actions.setToolActive,
      storeContexts: [],
      options: {},
    },
    setZoomTool: {
      commandFn: actions.setToolActive,
      storeContexts: [],
      options: { toolName: 'Zoom' },
    },
    setCornerstoneLayout: {
      commandFn: actions.setCornerstoneLayout,
      storeContexts: [],
      options: {},
      context: 'VIEWER',
    },
    setWindowLevel: {
      commandFn: actions.setWindowLevel,
      storeContexts: ['viewports'],
      options: {},
    },
    downloadDICOM: {
      commandFn: actions.downloadDICOM,
      storeContexts: ['viewports'],
      options: {},
    },
    downloadJPEG: {
      commandFn: actions.downloadJPEG,
      storeContexts: ['viewports'],
      options: {},
    },
  };

  return {
    actions,
    definitions,
    defaultContext: DEFAULT_CONTEXT,
  };
};

export default commandsModule;
